import { Component, _decorator, Vec3, EventTouch, screen, Touch, Quat, Vec2, Node, EventMouse, lerp, macro, input, Input, sys, tween, geometry, Camera, PhysicsSystem, Mat4, Color } from 'cc'
import { aa } from '../frame/FrameCore';
import HIT from './HitCtrl';
import { OrbitTarget } from './OrbitTarget';

const { ccclass, property, type, executeInEditMode } = _decorator;
let ray = new geometry.Ray();

let tempVec3 = new Vec3();
let tempVec3_2 = new Vec3();
let tempQuat = new Quat();
let tempVec2 = new Vec2();
let tempVec2_2 = new Vec2();
let rotateFactor = 1 / 150;
let scaleFactor = 1 / 200;
const intersectionPoint = new Vec3();

let _tempVec3: Vec3 = new Vec3;

let _tempM4: Mat4 = new Mat4;
const preTransforms = Object.freeze([
    Object.freeze([1, 0, 0, 1]), // SurfaceTransform.IDENTITY
    Object.freeze([0, 1, -1, 0]), // SurfaceTransform.ROTATE_90
    Object.freeze([-1, 0, 0, -1]), // SurfaceTransform.ROTATE_180
    Object.freeze([0, -1, 1, 0]), // SurfaceTransform.ROTATE_270
]);

let _tempOBB: geometry.OBB = new geometry.OBB;

@ccclass('OrbitCamera')
export default class OrbitCamera extends Component {

    @property enableTouch = true;
    @property enableScaleRadius = false;
    @property rotateSpeed = 1;
    @property followSpeed = 1;
    @property xRotationRange = new Vec2(5, 70);
    @property yRotationRange = new Vec2(0, 360);
    @property radiusRange = new Vec2(4, 5);

    @property radiusScaleSpeed = 1;

    @property followTargetRotationY = true;
    @property height = 4;
    private _cam: Camera;
    public target: Node;


    @property
    get radius() {
        return this._targetRadius;
    }
    set radius(v) {
        this._targetRadius = v;
    }

    @property({ type: OrbitTarget })
    get orbitTarget() {
        return this._oribitTarget;
    }
    set orbitTarget(v) {
        this._oribitTarget = v;
        this.target = this._oribitTarget.node;
        this._preview()

    }
    @property({ type: OrbitTarget })
    private _oribitTarget: OrbitTarget;



    @property
    get preview() {
        return false
    }
    set preview(v) {
        this._preview()
    }
    @property
    public xOffset = 0;

    @property
    get targetRotation(): Vec3 {
        return this._startRotation;
    }
    set targetRotation(v: Vec3) {
        this._targetRotation.set(v);
        this._startRotation.set(v);
    }


    @property({ visible: false }) private _startRotation = new Vec3;
    @property({ visible: false }) private _targetRadius = 10;

    private _center = new Vec3;
    private _targetCenter = new Vec3;
    private _touched = false;
    _targetRotation = new Vec3;
    private _rotation = new Quat
    private _radius = 10;
    private dis = 0;
    private _centerOffet = new Vec2(0, 0);
    private _scaleOffset = 0;
    private _shake = 0;

    set shake(v: number) {
        this._shake = v;
        this._scaleOffset = v * 0.6;
        this._centerOffet.x = v;
        this._centerOffet.y = v;
    }
    get shake() {
        return this._shake;
    }
    _preview() {
        const orbitTarget = this._oribitTarget;
        this.target = this._oribitTarget.node;
        this.xRotationRange = orbitTarget.xRotationRange;
        this.yRotationRange = orbitTarget.yRotationRange;
        this.radiusRange = orbitTarget.radiusRange;
        this.targetRotation = orbitTarget.targetRotation;
        this.height = orbitTarget.height;
        this.radius = orbitTarget.orbitRadius;
        this.resetTargetRotation();
        this.limitRotation();
        this.limitRadius();
        let targetRotation = this._targetRotation;
        Vec3.copy(this._targetCenter, this.target.worldPosition)
        this._targetCenter.y += this.height + this._centerOffet.y;
        this._targetCenter.x += this.xOffset + this._centerOffet.x;
        Quat.fromEuler(this._rotation, targetRotation.x, targetRotation.y, targetRotation.z);
        Vec3.transformQuat(tempVec3, Vec3.FORWARD, this._rotation);
        Vec3.multiplyScalar(tempVec3, tempVec3, this._targetRadius + this._scaleOffset)
        Vec3.add(tempVec3, tempVec3, this._targetCenter)
        this.node.position = tempVec3;
        this.node.lookAt(this._targetCenter);
    }

    start() {
        this._cam = this.node.getComponent(Camera);
        this.init();
    }

    shakeCam(v = 1, time = 1) {
        const self = this.getComponent(OrbitCamera);
        tween(self).to(0.04 * time, { shake: 0.5 * v }).to(0.06 * time, { shake: -0.5 * v }).to(0.03 * time, { shake: 0 }).start();
    }
    init() {
        macro.ENABLE_MULTI_TOUCH = true;
        input.on(Input.EventType.TOUCH_START, this.onTouchStart, this)
        input.on(Input.EventType.TOUCH_MOVE, this.onTouchMove, this)
        input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this)
        input.on(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this)
        if (this.enableScaleRadius && sys.platform == sys.Platform.DESKTOP_BROWSER) {
            input.on(Input.EventType.MOUSE_WHEEL, this.onMouseWheel, this)
        }
        this._preview();
    }
    resetGame() {
        this.radius = 2.5;
        this._targetRotation.set(35, 180, 0)
        this.xRotationRange.set(15, 50);
        this.limitRotation();
    }

    resetTargetRotation() {

        let targetRotation = this._targetRotation.set(this._startRotation);
        this.xRotationRange.set(20, 80);
        this.limitRotation();
        if (this.followTargetRotationY) {
            targetRotation = tempVec3_2.set(targetRotation);
            Quat.toEuler(tempVec3, this.target!.worldRotation);
            targetRotation.y += tempVec3.y;
        }
    }
    public rayIntersectGroundPlane(ray: geometry.Ray): Vec3 | null {
        // 对于y=0平面，法向量是(0,1,0)
        // 射线方向与平面法向量的点积就是ray.d.y
        const denominator = ray.d.y;

        // 如果射线平行于平面（射线方向y分量为0），返回null
        if (Math.abs(denominator) < 0.0001) {
            return null;
        }

        // 计算交点参数t
        // 对于y=0平面和射线起点ray.o，t = -ray.o.y / ray.d.y
        const t = -ray.o.y / denominator;

        // 如果t < 0，交点在射线起点后面
        if (t < 0) {
            return null;
        }
        // 计算交点：ray.o + t * ray.d
        intersectionPoint.x = ray.o.x + t * ray.d.x;
        intersectionPoint.y = 0; // 固定为0
        intersectionPoint.z = ray.o.z + t * ray.d.z;
        return intersectionPoint;
    }
    private _isDragging = false;
    private _draggedNode: Node = null;
    private _dragOffset = new Vec3(); // 拖拽时的偏移量


    onTouchStart(event?: EventTouch) {
        this._touched = true;
        if (aa.global.isGame) return;

        const x = event.getLocationX();
        const y = event.getLocationY();
        this._cam.screenPointToRay(x, y, ray);


        const hitNode = HIT.checkRayHit(ray);
        if (hitNode) {
            this._isDragging = true;
            this._draggedNode = hitNode;
            HIT.setNodeCtrl(hitNode)

            // 计算拖拽偏移量
            const groundPoint = this.rayIntersectGroundPlane(ray);
            if (groundPoint) {
                const nodePos = hitNode.worldPosition;
                Vec3.subtract(this._dragOffset, nodePos, groundPoint);
            }
            console.log("开始拖拽:", hitNode.name);
        }

    }

    onTouchMove(event?: EventTouch) {

        if (!this._touched) return;
        const touch = event.touch;
        // 如果正在拖拽物体
        if (this._isDragging) {
            const x = touch.getLocationX();
            const y = touch.getLocationY();
            this._cam.screenPointToRay(x, y, ray);
            // 计算射线与地面的交点
            const groundPoint = this.rayIntersectGroundPlane(ray);
            if (groundPoint) {
                // 应用偏移量并更新物体位置
                Vec3.add(_tempVec3, groundPoint, this._dragOffset);
                // 保持原有的y坐标
                const currentPos = this._draggedNode.worldPosition;
                _tempVec3.y = currentPos.y;
                this._draggedNode.setWorldPosition(_tempVec3);
                // 通知EventCtrl更新高亮
                HIT.showNodeBorder(this._draggedNode, Color.RED);
            }
            return; // 拖拽时不执行相机旋转
        }

        const touches = event.getAllTouches();
        /* scale radius for mobile multi touch */
        if (touches.length > 1) {
            const changedTouches = event.getTouches();
            let touch1: Touch = null!;
            let touch2: Touch = null!;
            if (changedTouches.length > 1) {
                touch1 = touches[0];
                touch2 = touches[1];
            } else {
                touch1 = touch;
                const diffID = touch1.getID();
                for (let i = 0; i < touches.length; i++) {
                    const element = touches[i];
                    if (element.getID() !== diffID) {
                        touch2 = element;
                        break;
                    }
                }
            }
            touch1.getLocation(tempVec2);
            if (!aa.global.isGame) {
                touch2.getLocation(tempVec2_2);
                let dis = Vec2.distance(tempVec2, tempVec2_2)
                let delta = dis - this.dis
                this._targetRadius += this.radiusScaleSpeed * -Math.sign(delta) * 0.3;
                this.limitRadius();
                this.dis = dis;
            } else {
                tempVec2 = touch!.getDelta().multiplyScalar(0.5);
                this.setRotate(tempVec2)
            }

        } else {
            tempVec2 = touch!.getDelta().multiplyScalar(0.5);
            this.setRotate(tempVec2)
        }

    }

    onTouchEnd(event?: EventTouch) {
        this._touched = false;
        this.dis = 0;  // 重置距离
        this._isDragging = false;
        if (aa.global.isGame) return;

        let x = event.getLocationX();
        let y = event.getLocationY();
        // console.log("cocos ray"+x+y)
        this._cam.screenPointToRay(x, y, ray)
        const endNode = HIT.checkRayHit(ray);
        if (!endNode) {
            HIT.clearClick(this._draggedNode);
        }
        this._draggedNode = null;

    }

    setRotate(v2: Vec2) {
        Quat.fromEuler(tempQuat, this._targetRotation.x, this._targetRotation.y, this._targetRotation.z);
        Quat.rotateX(tempQuat, tempQuat, -v2.y * rotateFactor);
        Quat.rotateAround(tempQuat, tempQuat, Vec3.UP, -v2.x * rotateFactor);
        Quat.toEuler(this._targetRotation, tempQuat);
        this.limitRotation()
    }

    onMouseWheel(event: EventMouse) {
        if (aa.global.isGame) return;

        let scrollY = event.getScrollY();
        this._targetRadius += this.radiusScaleSpeed * -Math.sign(scrollY);

        this.limitRadius();
    }
    limitRadius() {
        const max = Math.min(this.radiusRange.y, this._targetRadius)
        this._targetRadius = Math.max(this.radiusRange.x, max);
    }

    limitRotation() {
        let rotation = this._targetRotation;

        // 限制 x 轴旋转范围
        if (rotation.x < this.xRotationRange.x) {
            rotation.x = this.xRotationRange.x;
        } else if (rotation.x > this.xRotationRange.y) {
            rotation.x = this.xRotationRange.y;
        }
        // rotation.y = (rotation.y + 360) % 360;
        rotation.z = 0;

        if (this.yRotationRange.x != 0) {
            if (rotation.y < this.yRotationRange.x) {
                rotation.y = this.yRotationRange.x;
            } else if (rotation.y > this.yRotationRange.y) {
                rotation.y = this.yRotationRange.y;
            }
        }

    }

    lateUpdate(dt) {
        this.resetCam(dt);
        HIT.lateUpdate();
    }

    resetCam(dt) {
        if (!this.target) return;

        let targetRotation = this._targetRotation;
        Vec3.copy(this._targetCenter, this.target.worldPosition)
        this._targetCenter.y += this.height + this._centerOffet.y;
        this._targetCenter.x += this.xOffset + this._centerOffet.x;

        if (this.followTargetRotationY) {
            targetRotation = tempVec3_2.set(targetRotation);
            Quat.toEuler(tempVec3, this.target.worldRotation);
            targetRotation.y += tempVec3.y;
        }
        Quat.fromEuler(tempQuat, targetRotation.x, targetRotation.y, targetRotation.z);
        Quat.slerp(this._rotation, this._rotation, tempQuat, dt * 5 * this.rotateSpeed);
        Vec3.lerp(this._center, this._center, this._targetCenter, dt * 5 * this.followSpeed);
        this._radius = this._targetRadius + this._scaleOffset;
        Vec3.transformQuat(tempVec3, Vec3.FORWARD, this._rotation);
        Vec3.multiplyScalar(tempVec3, tempVec3, this._radius)
        Vec3.add(tempVec3, tempVec3, this._center)
        this.node.position = tempVec3;
        this.node.lookAt(this._center);
    }

}
